package oop.ex3.crosswords;

import java.util.*;

import oop.ex3.crosswords.CrosswordStructure.SlotType;
import oop.ex3.search.SearchBoardNode;

/**
 * File name: MyCrossword.java
 * Description: 
 * @author yossavi dawn16889
 */
public class MyCrossword implements Crossword{
	
	protected List<CrosswordEntry> movesRecord = new LinkedList<CrosswordEntry>();
	protected CrosswordGlossary glossary;
	protected CrosswordStructure structure;	
	protected List<String> terms= new LinkedList<String>();	
	protected CrosswordLetter fillboard[][];
	protected int bestSolution = 0;
	protected int qualityBound = 0;
	
	
	/**
	 * constructs an empty board
	 */
	public MyCrossword() {
		super();
	}
	
	/**
	 * constructor
	 * @param movesRecord
	 * @param glossary
	 * @param structure
	 * @param terms
	 * @param fillboard
	 * @param bestSolution
	 */
	public MyCrossword(List<CrosswordEntry> movesRecord,
			CrosswordGlossary glossary, CrosswordStructure structure,
			List<String> terms, CrosswordLetter[][] fillboard,
			int bestSolution) {
		this.attachGlossary(glossary);
		this.attachStructure(structure);
		this.movesRecord = movesRecord;
		this.glossary = glossary;
		this.structure = structure;
		this.terms = terms;
		this.fillboard = fillboard;
		this.bestSolution = bestSolution;
	}
	
	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#isBestSolution()
	 */
	@Override
	public boolean isBestSolution() {
		return (this.bestSolution==this.getQuality());
	}

	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#getMovesIterator()
	 */
	@Override
	public Iterator<CrosswordEntry> getMovesIterator() {
		EntryComparator comp = new EntryComparator();
		TreeSet<OverLapEntry> moves = new TreeSet<OverLapEntry>(comp);
		for(String term: this.terms) {
			boolean checkTerm = false;
			for(int i =0; i<this.fillboard.length; i++) {
				for(int j =0; j<this.fillboard[0].length; j++) {
					int letterCount=0;
					int overLap=0;
					while (letterCount<term.length() && this.fillboard.length>i+letterCount &&
							((this.fillboard[i+letterCount][j].getLetter() == term.charAt(letterCount)) || 
									this.fillboard[i+letterCount][j].getLetter() == ' ')) {
						if ((this.fillboard[i+letterCount][j].getLetter() == term.charAt(letterCount)))
							overLap++;
						letterCount++;						
					}
					
					if (letterCount==term.length()) {
						checkTerm = addToIterator(moves, term, i, j, overLap,false);
					}
					
					letterCount=0;
					overLap=0;
					while (letterCount<term.length() && this.fillboard[0].length>j+letterCount &&
							((this.fillboard[i][j+letterCount].getLetter() == term.charAt(letterCount)) || 
									this.fillboard[i][j+letterCount].getLetter() == ' ')) {
						if ((this.fillboard[i][j+letterCount].getLetter() == term.charAt(letterCount)))
							overLap++;
						letterCount++;
					}	
					
					if (letterCount==term.length()) {
						checkTerm = addToIterator(moves, term, i, j, overLap,true);
					}
					
				}
			}
			if(checkTerm)
				this.qualityBound+=term.length();
		}
		List<CrosswordEntry> entryMoves = new LinkedList<CrosswordEntry>();
		for (OverLapEntry move: moves)
			entryMoves.add(move.getEntry());
		return entryMoves.iterator();
	}
	
	/**
	 * @param moves
	 * @param term
	 * @param i
	 * @param j
	 * @param overLap
	 * @return
	 */
	private boolean addToIterator(TreeSet<OverLapEntry> moves, String term,
			int i, int j, int overLap, boolean isVertical) {
		MyCrosswordPosition position = new MyCrosswordPosition(i,j,isVertical);
		moves.add(new OverLapEntry(
				new MyCrosswordEntry(position, this.glossary.getTermDefinition(term), term), overLap));
		return true;
	}

	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#getQuality()
	 */
	@Override
	public int getQuality() {
		int counter = 0;
		if (this.movesRecord!=null) {
			for(CrosswordEntry word: this.movesRecord)
				counter+=word.getLength();
		}
		return counter;
	}

	
	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#getQualityBound()
	 */
	@Override
	public int getQualityBound() {
		return this.qualityBound;
	}

	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#doMove(oop.ex3.search.SearchMove)
	 */
	@Override
	public void doMove(CrosswordEntry move) {
		String word = move.getTerm();
		this.terms.remove(word);
		updateBoard(move, word, true);
		this.movesRecord.add(move);
	}
	
	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#undoMove(oop.ex3.search.SearchMove)
	 */
	@Override
	public void undoMove(CrosswordEntry move) {
		String word = move.getTerm();
		this.terms.add(word);
		updateBoard(move, word, false);
		// Update record
		this.movesRecord.remove(move);		
	}

	/**
	 * @param move
	 * @param word
	 */
	private void updateBoard(CrosswordEntry move, String word, boolean isChar) {
		for (int i=0; i<word.length(); i++) {
			int x = !move.getPosition().isVertical()? i:0;
			int y = move.getPosition().isVertical()? i:0;
			this.fillboard[move.getPosition().getX()+x][move.getPosition().getY()+y].setLetter(isChar?word.charAt(i):' ');
		}
	}
	
	/* (non-Javadoc)
	 * @see oop.ex3.search.SearchBoardNode#getCopy()
	 */
	@Override
	public SearchBoardNode<CrosswordEntry> getCopy() {
		List<String> terms = new LinkedList<String>();
		for (String term: this.terms)
			terms.add(term);
		
		CrosswordLetter fillboard[][]= new CrosswordLetter[this.fillboard.length][this.fillboard[0].length];
		for (int i = 0; i < this.fillboard.length; i++) {
			for (int j = 0; j < this.fillboard[0].length; j++) {
				fillboard[i][j] =new CrosswordLetter(this.fillboard[i][j]);
			}
		}
		
		List<CrosswordEntry> movesRecord= new LinkedList<CrosswordEntry>();
		for (CrosswordEntry word: this.movesRecord)
			movesRecord.add(word);
		
		return new MyCrossword(movesRecord, this.glossary, this.structure,terms, fillboard, this.bestSolution);
	}

	/* (non-Javadoc)
	 * @see oop.ex3.crosswords.Crossword#attachGlossary(oop.ex3.crosswords.CrosswordGlossary)
	 */
	@Override
	public void attachGlossary(CrosswordGlossary glossary) {
		this.glossary=glossary;
		Set<String> tempTerms = this.glossary.getTerms();
		for(String term: tempTerms){
			this.terms.add(term.toLowerCase());
			this.bestSolution+=term.length();
		}	
	}

	/* (non-Javadoc)
	 * @see oop.ex3.crosswords.Crossword#attachStructure(oop.ex3.crosswords.CrosswordStructure)
	 */
	@Override
	public void attachStructure(CrosswordStructure structure) {
		this.structure=structure;
		this.fillboard = new CrosswordLetter[this.structure.getWidth()][this.structure.getHeight()];
		for (int x=0; x<this.structure.getWidth(); x++) {
			for (int y=0; y<this.structure.getHeight(); y++) {
				CrosswordPosition tempPos=new MyCrosswordPosition(x, y, false);
				if (this.structure.getSlotType(tempPos)==SlotType.UNUSED_SLOT)
					this.fillboard[x][y]=new CrosswordLetter(' ');
				else
					this.fillboard[x][y]=new CrosswordLetter('#');
			}
		}
	}
		
	/* (non-Javadoc)
	 * @see oop.ex3.crosswords.Crossword#getCrosswordEntries()
	 */
	@Override
	public Collection<CrosswordEntry> getCrosswordEntries() {
		return this.movesRecord;
	}
	

}
